Node.js教程-Runoob

toc and abstract.

菜鸟:http://www.runoob.com/nodejs/nodejs-tutorial.html

2017.1.1 星期日 15:10

一 教程

简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

  • Hello World
    1. 脚本模式
    2. 交互模式

二 安装配置

LTS

  • Windows安装

    1. 安装包 .msi

      检测PATH环境变量是否配置了Node.js,点击开始=》运行=》输入”cmd” => 输入命令”path”,输出如下结果:

    2. 二进制文件 (.exe)安装

    3. 版本测试
  • Ubuntu
  • CentOS

三 创建第一个应用

不过对 Node.js 来说,概念完全不一样了。使用 Node.js 时,我们不仅仅 在实现一个应用,同时还实现了整个 HTTP 服务器。

var http = require('http');

http.createServer(function (request, response) {

    // 发送 HTTP 头部 
    // HTTP 状态值: 200 : OK
    // 内容类型: text/plain
    response.writeHead(200, {'Content-Type': 'text/plain'});

    // 发送响应数据 "Hello World"
    response.end('Hello World\n');
}).listen(8888);

// 终端打印如下信息
console.log('Server running at http://127.0.0.1:8888/');

让我们先了解下 Node.js 应用是由哪几部分组成的:

  1. 引入 required 模块:我们可以使用 require 指令来载入 Node.js 模块。
  2. 创建服务器:服务器可以监听客户端的请求,类似于 Apache 、Nginx 等 HTTP 服务器。
  3. 接收请求与响应请求 服务器很容易创建,客户端可以使用浏览器或终端发送 HTTP

    四 NPM 使用介绍

    NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种:
  • 允许用户从NPM服务器下载别人编写的第三方包到本地使用。
  • 允许用户从NPM服务器下载并安装别人编写的命令行程序到本地使用。
  • 允许用户将自己编写的包或命令行程序上传到NPM服务器供别人使用。

由于新版的nodejs已经集成了npm,所以之前npm也一并安装好了。同样可以通过输入 npm -v 来测试是否成功安装。

如果你安装的是旧版本的 npm,可以很容易得通过 npm 命令来升级,命令如下:npm install npm -g
使用淘宝镜像的命令:cnpm install npm -g

1 使用 npm 命令安装模块

$ npm install express

2 全局安装与本地安装

$ npm install express -g
  • 全局安装:
  1. 将安装包放在 /usr/local 下或者你 node 的安装目录。
  2. 可以直接在命令行里使用。
  • 查看安装信息

    //查看所有全局安装的模块
    $ npm list -g
    //某模块的版本号
    $ npm list grunt
    

    3 使用 package.json

    Package.json 属性说明

    name,version,description,homepage,author,contributors,dependencies,repository,main,keywords

    4 卸载模块

    $ npm uninstall express

    5 更新

    $ npm update express

    6 搜索

    $ npm search express

    7 创建

    //生成package.json
    $ npm init
    //添加用户
    $ npm adduser
    //发布
    $ npm publish

    8 版本号

    X.Y.Z:主版本号,次版本号,补丁版本号

版本号有了这个保证后,在申明第三方包依赖时,除了可依赖于一个固定版本号外,还可依赖于某个范围的版本号。例如”argv”: “0.0.x”表示依赖于0.0.x系列的最新版argv。

9 NPM 常用命令

10 使用淘宝 NPM 镜像

$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ cnpm install [name]

五 REPL

Node.js REPL(Read Eval Print Loop:交互式解释器) 表示一个电脑的环境,类似 Window 系统的终端或 Unix/Linux shell,我们可以在终端中输入命令,并接收系统的响应。

Node 自带了交互式解释器,可以执行以下任务:
读取 、执行 、打印、 循环:循环操作以上步骤直到用户两次按下 ctrl-c 按钮退出。

1 启动 $ node

  1. 简单表达式运算
  2. 使用变量
  3. 多行表达式
  4. 下划线(_)变量

    var x=10,y=20;
    x+y
    var sum=_
    console.log(sum)//30
    

2 REPL 命令

ctrl+c,ctrl+c两次、ctrl+d,向上/下,tab,.help,.break,.clear,.save filename,.load filename

3 停止 REPL

两次ctrl+c

六 回调函数

  1. Node.js 异步编程的直接体现就是回调。
  2. 异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。
  3. 回调函数在完成任务后就会被调用,Node 使用了大量的回调函数,Node 所有 API 都支持回调函数。

例如,我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。

1 阻塞代码实例

var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程序执行结束!");

2 非阻塞代码实例

var fs = require("fs");
fs.readFile('input.txt', function (err, data) {
    if (err) return console.error(err);
    console.log(data.toString());
});
console.log("程序执行结束!");

因此,阻塞是按顺序执行的,而非阻塞是不需要按顺序的,所以如果需要处理回调函数的参数,我们就需要写在回调函数内。

七 事件循环

  1. Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。
  2. Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。
  3. Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
  4. Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

    1 事件驱动程序

  5. Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
  6. 当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
  • 这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)
  • 事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数
    图例:http://www.runoob.com/nodejs/nodejs-event-loop.html

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

// 引入 events 模块
var events = require('events');
// 创建 eventEmitter 对象
var eventEmitter = new events.EventEmitter();
// 绑定事件及事件的处理程序
eventEmitter.on('eventName', eventHandler);
// 通过程序触发事件:
eventEmitter.emit('eventName');

实例

2 Node 应用程序是如何工作的?

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

八 EventEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

1 EventEmitter 类

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。

  1. 你可以通过require(“events”);来访问该模块。

    // 引入 events 模块  
    var events = require('events');  
    // 创建 eventEmitter 对象
    var eventEmitter = new events.EventEmitter();
    
  2. EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。

    下面我们用一个简单的例子说明 EventEmitter 的用法:

  3. EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。
    当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

    var events = require('events'); 
    var emitter = new events.EventEmitter(); 
    emitter.on('someEvent', function(arg1, arg2) { 
        console.log('listener1', arg1, arg2); 
    }); 
    emitter.on('someEvent', function(arg1, arg2) { 
        console.log('listener2', arg1, arg2); 
    }); 
    emitter.emit('someEvent', 'arg1 参数', 'arg2 参数'); 
    
  4. EventEmitter 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。
    • 方法:addListener(event,listener),on(event,listener),once(v,l),removeListener(e,l),removeAllListener([e]),setMaxListeners(n),listeners(e),emit(e,[arg1],[arg2],[…])
    • 类方法:listenerCount(emitter,event)
    • 事件:newListener,removeListener,

2 error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。

当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。

我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

3 继承 EventEmitter

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

为什么要这样做呢?原因有两点:

  1. 首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。
  2. 其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

九 Buffer

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。

1 创建 Buffer 类

var buf = new Buffer(10);
var buf = new Buffer([10, 20, 30, 40, 50]);
var buf = new Buffer("www.runoob.com", "utf-8");

2 写入缓冲区

buf.write(string[, offset[, length]][, encoding])

3 从缓冲区读取数据

buf.toString([encoding[, start[, end]]])

4 将 Buffer 转换为 JSON 对象

buf.toJSON()    

5 缓冲区合并

Buffer.concat(list[, totalLength])    

6 缓冲区比较

buf.compare(otherBuffer);

7 拷贝缓冲区

buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]])

8 缓冲区裁剪

9 缓冲区长度

10 方法参考手册

十 Stream

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。

Node.js,Stream 有四种流类型:

  • Readable - 可读操作。
  • Writable - 可写操作。
  • Duplex - 可读可写操作.
  • Transform - 操作被写入数据,然后读出结果。

所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:

  • data - 当有数据可读时触发。
  • end - 没有更多的数据可读时触发。
  • error - 在接收和写入过程中发生错误时触发。
  • finish - 所有数据已被写入到底层系统时触发。

    1 从流中读取数据

    var fs = require(“fs”);
    var data = ‘’;
    // 创建可读流
    var readerStream = fs.createReadStream(‘input.txt’);
    // 设置编码为 utf8。
    readerStream.setEncoding(‘UTF8’);
    // 处理流事件 –> data, end, and error
    readerStream.on(‘data’, function(chunk) {
    data += chunk;
    });
    readerStream.on(‘end’,function(){
    console.log(data);
    });
    readerStream.on(‘error’, function(err){
    console.log(err.stack);
    });
    console.log(“程序执行完毕”);

    2 写入流

    var fs = require(“fs”);
    var data = ‘菜鸟教程官网地址:www.runoob.com';
    // 创建一个可以写入的流,写入到文件 output.txt 中
    var writerStream = fs.createWriteStream(‘output.txt’);
    // 使用 utf8 编码写入数据
    writerStream.write(data,’UTF8’);
    // 标记文件末尾
    writerStream.end();
    // 处理流事件 –> data, end, and error
    writerStream.on(‘finish’, function() {

    console.log("写入完成。");
    

    });
    writerStream.on(‘error’, function(err){
    console.log(err.stack);
    });
    console.log(“程序执行完毕”);

    3 管道流

    // 管道读写操作
    // 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
    readerStream.pipe(writerStream);

    4 链式流

    链式是通过连接输出流到另外一个流并创建多个对个流操作链的机制。链式流一般用于管道操作。
    接下来我们就是用管道和链式来压缩和解压文件。

    var fs = require(“fs”);
    var zlib = require(‘zlib’);

    // 压缩 input.txt 文件为 input.txt.gz
    fs.createReadStream(‘input.txt’)
    .pipe(zlib.createGzip())
    .pipe(fs.createWriteStream(‘input.txt.gz’));
    console.log(“文件压缩完成。”);

    // 解压 input.txt.gz 文件为 input.txt
    fs.createReadStream(‘input.txt.gz’)
    .pipe(zlib.createGunzip())
    .pipe(fs.createWriteStream(‘input.txt’));

    console.log(“文件解压完成。”);

十一 模块系统

为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。

模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是JavaScript 代码、JSON 或者编译过的C/C++ 扩展

1 创建模块

//1 
var hello = require('./hello');
hello.world();
//接下来我们就来创建 hello.js 文件,代码如下:
exports.world = function() {
    console.log('Hello World');
}

在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require(‘./hello’) 加载这个模块,然后就可以直接访 问 hello.js 中 exports 对象的成员函数了。

2 有时候我们只是想把一个对象封装到模块中

function Hello() { 
    var name; 
    this.setName = function(thyName) { 
        name = thyName; 
    }; 
    this.sayHello = function() { 
        console.log('Hello ' + name); 
    }; 
}; 
module.exports = Hello;
//main.js 
var Hello = require('./hello'); 
hello = new Hello(); 
hello.setName('BYVoid'); 
hello.sayHello(); 

模块接口的唯一变化是使用 module.exports = Hello 代替了exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports。

2 服务端的模块放在哪里

Node.js 的 require 方法中的文件查找策略如下:
由于 Node.js 中存在 4 类模块(原生模块和3种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:

  • 从文件模块缓存中加载
  • 从原生模块加载
  • 从文件加载
    require方法接受以下几种参数的传递:
    • http、fs、path等,原生模块。
    • ./mod或../mod,相对路径的文件模块。
    • /pathtomodule/mod,绝对路径的文件模块。
    • mod,非原生模块的文件模块。

十二 函数

在JavaScript中,一个函数可以作为另一个函数的参数。我们可以先定义一个函数,然后传递,也可以在传递参数的地方直接定义函数。
Node.js中函数的使用与Javascript类似,举例来说,你可以这样做:

1 匿名函数

2 函数传递是如何让HTTP服务器工作的

var http = require("http");
http.createServer(function(request, response) {
    response.writeHead(200, {"Content-Type": "text/plain"});
    response.write("Hello World");
    response.end();
}).listen(8888);

十三 路由

  1. 我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码。

  2. 因此,我们需要查看 HTTP 请求,从中提取出请求的 URL 以及 GET/POST 参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能。

我们需要的所有数据都会包含在 request 对象中,该对象作为 onRequest() 回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的 Node.JS 模块,它们分别是 url 和 querystring 模块。
当然我们也可以用 querystring 模块来解析 POST 请求体中的参数,稍后会有演示。

  1. 现在我们来给 onRequest() 函数加上一些逻辑,用来找出浏览器请求的 URL 路径:

    好了,我们的应用现在可以通过请求的 URL 路径来区别不同请求了–这使我们得以使用路由(还未完成)来将请求以 URL 路径为基准映射到处理程序上。

    在我们所要构建的应用中,这意味着来自 /start 和 /upload 的请求可以使用不同的代码来处理。稍后我们将看到这些内容是如何整合到一起的。

  2. 现在我们可以来编写路由了,建立一个名为 router.js 的文件,添加以下内容:
  3. 如你所见,这段代码什么也没干,不过对于现在来说这是应该的。在添加更多的逻辑以前,我们先来看看如何把路由和服务器整合起来。

    我们的服务器应当知道路由的存在并加以有效利用。我们当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是其它语言的编程经验告诉我们这会是一件非常痛苦的事,因此我们将使用依赖注入的方式较松散地添加路由模块。

    1. 首先,我们来扩展一下服务器的 start() 函数,以便将路由函数作为参数传递过去,server.js 文件代码如下
    2. 同时,我们会相应扩展 index.js,使得路由函数可以被注入到服务器中:
    3. 在这里,我们传递的函数依旧什么也没做。

      如果现在启动应用(node index.js,始终记得这个命令行),随后请求一个URL,你将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由:

    4. 以上输出已经去掉了比较烦人的 /favicon.ico 请求相关的部分。
      浏览器访问 http://127.0.0.1:8888/,输出结果如下:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      //server.js 
      var http = require("http");
      var url = require("url");
      function start(route) {
      function onRequest(request, response) {
      var pathname = url.parse(request.url).pathname;
      console.log("Request for " + pathname + " received.");
      //3.1
      route(pathname);

      response.writeHead(200, {"Content-Type": "text/plain"});
      response.write("Hello World");
      response.end();
      }
      http.createServer(onRequest).listen(8888);
      console.log("Server has started.");
      }
      exports.start = start;
      //2 router.js 文件代码:
      function route(pathname) {
      console.log("About to route a request for " + pathname);
      }
      exports.route = route;
      //3.2 index.js 文件代码:
      var server = require("./server");
      var router = require("./router");
      server.start(router.route);

十四 全局对象

JavaScript 中有一个特殊的对象,称为全局对象(Global Object),它及其所有属性都可以在程序的任何地方访问,即全局变量。

在浏览器 JavaScript 中,通常 window 是全局对象, 而 Node.js 中的全局对象是 global,所有全局变量(除了 global 本身以外)都是 global 对象的属性。
在 Node.js 我们可以直接访问到 global 的属性,而不需要在应用中包含它。

1 全局对象与全局变量

global 最根本的作用是作为全局变量的宿主。按照 ECMAScript 的定义,满足以下条 件的变量是全局变量:

  • 在最外层定义的变量;
  • 全局对象的属性;
  • 隐式定义的变量(未定义直接赋值的变量)。

当你定义一个全局变量时,这个变量同时也会成为全局对象的属性,反之亦然。需要注 意的是,在 Node.js 中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的, 而模块本身不是最外层上下文。

注意: 永远使用 var 定义变量以避免引入全局变量,因为全局变量会污染 命名空间,提高代码的耦合风险。

1 _filename

2 _dirname

3.1 setTimeout(cb, ms)

3.2 clearTimeout(t)

4 setInterval(cb, ms)

5 console

console 用于提供控制台标准输出,它是由 Internet Explorer 的 JScript 引擎提供的调试工具,后来逐渐成为浏览器的事实标准。

Node.js 沿用了这个标准,提供与习惯行为一致的 console 对象,用于向标准输出流(stdout)或标准错误流(stderr)输出字符。

console 方法:log,info,error,warn,dir,time,timeEnd,trace,assert

6 process

process 是一个全局变量,即 global 对象的属性。
它用于描述当前Node.js 进程状态的对象,提供了一个与操作系统的简单接口。通常在你写本地命令行程序的时候,少不了要 和它打交道。下面将会介绍 process 对象的一些最常用的成员方法:exit,before,Exit,uncaughtException,Signal

  • 实例

    process.on('exit', function(code) {
    // 以下代码永远不会执行
    setTimeout(function() {
        console.log("该代码不会执行");
    }, 0);
    
    console.log('退出码为:', code);
    });
    console.log("程序执行结束");
    
  • 退出状态码
  • Process 属性
    。。
  • 方法参考手册

十五 常用工具

util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足。

1 util.inherits

util.inherits(constructor, superConstructor)是一个实现对象间原型继承 的函数。

JavaScript 的面向对象特性是基于原型的,与常见的基于类的不同。JavaScript 没有 提供对象继承的语言级别特性,而是通过原型复制来实现的。

2 util.inspect

3 util.isArray(object)

4 util.isRegExp(object)

5 util.isDate(object)

6 util.isError(object)

十六 文件系统

十七 GET/POST请求

十八 工具模块

1 OS 模块

提供基本的系统操作函数。

2 Path 模块

提供了处理和转换文件路的工具。

3 Net 模块

用于底层的网络通信。提供了服务端和客户端的的操作。

4 DNS 模块

用于解析域名。

5 Domain 模块

简化异步代码的异常处理,可以捕捉处理try catch无法捕捉的。

十九 Web模块

1 什么是 Web 服务器?

Web服务器一般指网站服务器,是指驻留于因特网上某种类型计算机的程序,Web服务器的基本功能就是提供Web信息浏览服务。它只需支持HTTP协议、HTML文档格式及URL,与客户端的网络浏览器配合。

大多数 web 服务器都支持服务端的脚本语言(php、python、ruby)等,并通过脚本语言从数据库获取数据,将结果返回给客户端浏览器。

目前最主流的三个Web服务器是Apache、Nginx、IIS。

2 Web 应用架构

  • Client - 客户端,一般指浏览器,浏览器可以通过 HTTP 协议向服务器请求数据。
  • Server - 服务端,一般指 Web 服务器,可以接收客户端请求,并向客户端发送响应数据。
  • Business - 业务层, 通过 Web 服务器处理应用程序,如与数据库交互,逻辑运算,调用外部程序等。
  • Data - 数据层,一般由数据库组成。

    3 使用 Node 创建 Web 服务器

    Node.js 提供了 http 模块,http 模块主要用于搭建 HTTP 服务端和客户端,使用 HTTP 服务器或客户端功能必须调用 http 模块,代码如下:

    var http = require(‘http’);
    var fs = require(‘fs’);
    var url = require(‘url’);
    // 创建服务器
    http.createServer( function (request, response) {
    // 解析请求,包括文件名
    var pathname = url.parse(request.url).pathname;
    // 输出请求的文件名
    console.log(“Request for “ + pathname + “ received.”);
    // 从文件系统中读取请求的文件内容
    fs.readFile(pathname.substr(1), function (err, data) {

    if (err) {
        console.log(err);
        // HTTP 状态码: 404 : NOT FOUND
        // Content Type: text/plain
        response.writeHead(404, {'Content-Type': 'text/html'});
    }else{             
        // HTTP 状态码: 200 : OK
        // Content Type: text/plain
        response.writeHead(200, {'Content-Type': 'text/html'});    
    
        // 响应文件内容
        response.write(data.toString());        
    }
    //  发送响应数据
    response.end();
    

    });
    }).listen(8081);

    // 控制台会输出以下信息
    console.log(‘Server running at http://127.0.0.1:8081/');

    4 使用 Node 创建 Web 客户端

    var http = require(‘http’);
    // 用于请求的选项
    var options = {
    host: ‘localhost’,
    port: ‘8081’,
    path: ‘/index.htm’
    };
    // 处理响应的回调函数
    var callback = function(response){
    // 不断更新数据
    var body = ‘’;
    response.on(‘data’, function(data) {

    body += data;
    

    });
    response.on(‘end’, function() {

    // 数据接收完成
    console.log(body);
    

    });
    }
    // 向服务端发送请求
    var req = http.request(options, callback);
    req.end();

二十 Express框架

1 Express 简介

Express 是一个简洁而灵活的 node.js Web应用框架, 提供了一系列强大特性帮助你创建各种 Web 应用,和丰富的 HTTP 工具。

使用 Express 可以快速地搭建一个完整功能的网站。

Express 框架核心特性:

  1. 可以设置中间件来响应 HTTP 请求。
  2. 定义了路由表用于执行不同的 HTTP 请求动作。
  3. 可以通过向模板传递参数来动态渲染 HTML 页面。

    2 安装 Express

    以下几个重要的模块是需要与 express 框架一起安装的:
  • body-parser - node.js 中间件,用于处理 JSON, Raw, Text 和 URL 编码的数据。
  • cookie-parser - 这就是一个解析Cookie的工具。通过req.cookies可以取到传过来的cookie,并把它们转成对象。
  • multer - node.js 中间件,用于处理 enctype=”multipart/form-data”(设置表单的MIME编码)的表单数据。

    3 第一个 Express 框架实例

    //express_demo.js 文件
    var express = require(‘express’);
    var app = express();
    app.get(‘/‘, function (req, res) {
    res.send('Hello World');
    
    })
    var server = app.listen(8081, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("应用实例,访问地址为 http://%s:%s", host, port)
    
    })

    4 请求和响应

    Express 应用使用回调函数的参数: request 和 response 对象来处理请求和响应的数据。

    1.Request 对象 - request 对象表示 HTTP 请求,

    包含了请求查询字符串,参数,内容,HTTP 头部等属性。常见属性有:

    2.Response 对象 - response 对象表示 HTTP 响应,

    即在接收到请求时向客户端发送的 HTTP 响应数据。常见属性有:

    5 路由

    我们已经了解了 HTTP 请求的基本应用,而路由决定了由谁(指定脚本)去响应客户端请求。

在HTTP请求中,我们可以通过路由提取出请求的URL以及GET/POST参数。

6 静态文件

Express 提供了内置的中间件 express.static 来设置静态文件如:图片, CSS, JavaScript 等。

你可以使用 express.static 中间件来设置静态文件路径。例如,如果你将图片, CSS, JavaScript 文件放在 public 目录下,你可以这么写:

app.use(express.static('public'));

在浏览器中访问 http://127.0.0.1:8081/images/logo.png

7 GET 方法

以下实例演示了在表单中通过 GET 方法提交两个参数,我们可以使用 server.js 文件内的 process_get 路由器来处理输入:

<html>
<body>
<form action="http://127.0.0.1:8081/process_get" method="GET">
First Name: <input type="text" name="first_name">  <br>

Last Name: <input type="text" name="last_name">
<input type="submit" value="Submit">
</form>
</body>
</html>
//
var express = require('express');
var app = express();
app.use(express.static('public'));
app.get('/index.htm', function (req, res) {
    res.sendFile( __dirname + "/" + "index.htm" );
})
app.get('/process_get', function (req, res) {
    // 输出 JSON 格式
    var response = {
        "first_name":req.query.first_name,
        "last_name":req.query.last_name
    };
    console.log(response);
    res.end(JSON.stringify(response));
})
var server = app.listen(8081, function () {
    var host = server.address().address
    var port = server.address().port
    console.log("应用实例,访问地址为 http://%s:%s", host, port)
})   

8 POST 方法

以下实例演示了在表单中通过 POST 方法提交两个参数,我们可以使用 server.js 文件内的 process_post 路由器来处理输入:

9 文件上传

以下我们创建一个用于上传文件的表单,使用 POST 方法,表单 enctype 属性设置为 multipart/form-data。

我们可以使用中间件向 Node.js 服务器发送 cookie 信息,以下代码输出了客户端发送的 cookie 信息:

// express_cookie.js 文件
var express      = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get('/', function(req, res) {
console.log("Cookies: ", req.cookies)
})
app.listen(8081)

二一 RESTful API

  • 什么是 REST?
    REST即表述性状态传递(英文:Representational State Transfer,简称REST)是Roy Fielding博士在2000年他的博士论文中提出来的一种软件架构风格。

    表述性状态转移是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。需要注意的是,REST是设计风格而不是标准。REST通常基于使用HTTP,URI,和XML(标准通用标记语言下的一个子集)以及HTML(标准通用标记语言下的一个应用)这些现有的广泛流行的协议和标准。REST 通常使用 JSON 数据格式。

  • HTTP 方法
    以下为 REST 基本架构的四个方法:
  1. GET - 用于获取数据。
  2. PUT - 用于更新或添加数据。
  3. DELETE - 用于删除数据。
  4. POST - 用于添加数据。

    1 RESTful Web Services

    Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,可使用开放的XML(标准通用标记语言下的一个子集)标准来描述、发布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。

基于 REST 架构的 Web Services 即是 RESTful。

由于轻量级以及通过 HTTP 直接传输数据的特性,Web 服务的 RESTful 方法已经成为最常见的替代方法。可以使用各种语言(比如 Java 程序、Perl、Ruby、Python、PHP 和 Javascript[包括 Ajax])实现客户端。

RESTful Web 服务通常可以通过自动客户端或代表用户的应用程序访问。但是,这种服务的简便性让用户能够与之直接交互,使用它们的 Web 浏览器构建一个 GET URL 并读取返回的内容。

2 创建 RESTful

  1. 获取用户列表:RESTful API listUsers
  2. 添加用户: RESTful API addUser
  3. 显示用户详情: RESTful API :id(用户id)
  4. 删除用户: RESTful API deleteUser

二二 多进程

我们都知道 Node.js 是以单线程的模式运行的,但它使用的是事件驱动来处理并发,这样有助于我们在多核 cpu 的系统上创建多个子进程,从而提高性能。

每个子进程总是带有三个流对象:child.stdin, child.stdout 和child.stderr。他们可能会共享父进程的 stdio 流,或者也可以是独立的被导流的流对象。

Node 提供了 child_process 模块来创建子进程,方法有:

  • exec - child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。
  • spawn - child_process.spawn 使用指定的命令行参数创建新进程。
  • fork - child_process.fork 是 spawn()的特殊形式,用于在子进程中运行的模块,如 fork(‘./son.js’) 相当于 spawn(‘node’, [‘./son.js’]) 。与spawn方法不同的是,fork会在父进程与子进程之间,建立一个通信管道,用于进程之间的通信。

    1 exec() 方法

    child_process.exec(command[, options], callback)
    child_process.exec 使用子进程执行命令,缓存子进程的输出,并将子进程的输出以回调函数参数的形式返回。

    2 spawn() 方法

    child_process.spawn(command[, args][, options])

    3 fork 方法

    child_process.fork(modulePath[, args][, options])
    child_process.fork 是 spawn() 方法的特殊形式,用于创建进程

二三 JXcore打包

Node.js 是一个开放源代码、跨平台的、用于服务器端和网络应用的运行环境。

JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线程运行。

二四 MySQL

  • 安装驱动

    $ cnpm install mysql
    
  • 连接数据库

    var mysql      = require('mysql');
    

    var connection = mysql.createConnection({
    host : ‘localhost’,
    user : ‘root’,
    password : ‘123456’,
    database : ‘test’
    });

    connection.connect();

    connection.query(‘SELECT 1 + 1 AS solution’, function (error, results, fields) {
    if (error) throw error;
    console.log(‘The solution is: ‘, results[0].solution);
    });

    数据库操作( CURD )

  1. 查询数据
    将上面我们提供的 SQL 文件导入数据库后,执行以下代码即可查询出数据:

    var mysql  = require('mysql');  
    var connection = mysql.createConnection({     
        host     : 'localhost',       
        user     : 'root',              
        password : '123456',       
        port: '3306',                   
        database: 'test', 
    }); 
    connection.connect();
    var  sql = 'SELECT * FROM websites';
    //查
    connection.query(sql,function (err, result) {
        if(err){
            console.log('[SELECT ERROR] - ',err.message);
            return;
        }
        console.log('-----------------SELECT-------------');
        console.log(result);
        console.log('-------------------------------\n\n');  
    });
    connection.end();    
    
  2. 插入数据

    var  addSql = 'INSERT INTO websites(Id,name,url,alexa,country) VALUES(0,?,?,?,?)';
    var  addSqlParams = ['菜鸟工具', 'https://c.runoob.com','23453', 'CN'];
    //增
    connection.query(addSql,addSqlParams,function (err, result) {
        ..
    }    
    
  3. 更新数据

    var modSql = 'UPDATE websites SET name = ?,url = ? WHERE Id = ?';
    var modSqlParams = ['菜鸟移动站', 'https://m.runoob.com',6];
    //改
    connection.query(modSql,modSqlParams,function (err, result) {        
    
  4. 删除数据

    var delSql = 'DELETE FROM websites where id=6';
    //删
    connection.query(delSql,function (err, result) {
    

二五 MongoDB

MongoDB是一种文档导向数据库管理系统,由C++撰写而成。

  • 安装驱动

    $ cnpm install mongodb  
    

    接下来我们来实现增删改查功能。

    数据库操作( CURD )

  1. 插入数据

    var MongoClient = require('mongodb').MongoClient;
    var DB_CONN_STR = 'mongodb://localhost:27017/runoob'; //# 数据库为 runoob
    
    var insertData = function(db, callback) {  
        //连接到表 site
        var collection = db.collection('site');
        //插入数据
        var data = [{"name":"菜鸟教程","url":"www.runoob.com"},{"name":"菜鸟工具","url":"c.runoob.com"}];
        collection.insert(data, function(err, result) { 
            if(err)
            {
                console.log('Error:'+ err);
                return;
            }     
            callback(result);
        });
    }
    
    MongoClient.connect(DB_CONN_STR, function(err, db) {
        console.log("连接成功!");
        insertData(db, function(result) {
            console.log(result);
            db.close();
        });
    });
    
  2. 查询数据

    var selectData = function(db, callback) {  
        //连接到表  
        var collection = db.collection('site');
        //查询数据
        var whereStr = {"name":'菜鸟教程'};
        collection.find(whereStr).toArray(function(err, result) {
            if(err)
            {
                console.log('Error:'+ err);
                return;
            }     
            callback(result);
        });
    }
    
  3. 更新数据

    var updateData = function(db, callback) {  
        //连接到表  
        var collection = db.collection('site');
        //更新数据
        var whereStr = {"name":'菜鸟教程'};
        var updateStr = {$set: { "url" : "https://www.runoob.com" }};
        collection.update(whereStr,updateStr, function(err, result) {
            if(err)
            {
                console.log('Error:'+ err);
                return;
            }     
            callback(result);
        });
    }
    
  4. 删除数据

    var delData = function(db, callback) {  
        //连接到表  
        var collection = db.collection('site');
        //删除数据
        var whereStr = {"name":'菜鸟工具'};
        collection.remove(whereStr, function(err, result) {
            if(err)
            {
            console.log('Error:'+ err);
            return;
            }     
            callback(result);
        });
    }
    

home 2017.1.1 日 22:53

目录章节 状态 截止时间 后续安排
note /// —- —-
code /// —- —-
14 全局对象 前 概念性知识,重在理解 —- —-
14 全局对象 后 应用性,实践,基本简单过一遍,有的没有过 2017.10.1 —-
16 文件系统 没有过 2017.10.1 neededThenWork
17 GET/POST请求 没有过 2017.10.1 neededThenWork
19 Web模块中文件(上传) 没有过 2017.10.1 neededThenWork
18 工具模块 5个 没有过 2017.10.1 neededThenWork
—- A 比较重要,偏实践 —- —-
15 常用工具util 大概过了一下 2017.10.1 neededThenWork
24 MySQL 大概过了一下 2017.10.1 neededThenWork
25 MongoDB 大概过了一下 2017.10.1 neededThenWork
20 Express框架 大概过了一下 2017.10.1 neededThenWork
—- B 比较重要,偏理论 —- —-
19 Web模块 大概过了一下 2017.10.1 neededThenWork
21 RESTful API 大概过了一下 2017.10.1 neededThenWork
22 多线程 大概过了一下 2017.10.1 neededThenWork
23 JXcore 大概过了一下,根本不知道什么东西 2017.10.1 neededThenWork
—- —- —- —-
—- —- —- —-
—- —- —- —-

总结:

  1. 基本上都过了一遍,知道了。
  2. 知道了,不等于 掌握、明白;离实际操作还有差别。
  3. 还有一些没有过的,比如文件,get/post等;
    还有一些没看明白的(JXcore),和没有看的部分(工具模块5个)。
  • 掌握nodejs 基本原理,常见操作,为以后熟练应用(具体某一块)打基础

其他:http://www.yiibai.com/nodejs/

knowledge is no pay,reward is kindness
0%